package com.alangeorge.web.bloodhound.model;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;

import static com.alangeorge.web.bloodhound.model.DistanceUnit.*;

@SuppressWarnings("UnusedDeclaration")
@Entity
@Table(name = "location")
@NamedQueries({
        @NamedQuery(name="Location.findByDeviceId", query = "select l from Location l where deviceId = :deviceId order by l.createTime desc, l.id desc"),
        @NamedQuery(name="Location.countByDeviceId", query = "select count(l.id) from Location l where deviceId = :deviceId")
})

public class Location {
    @Id // @Id indicates that this it a unique primary key
    @GeneratedValue(strategy = GenerationType.IDENTITY) // @GeneratedValue indicates that value is automatically generated by the server
    @Column(name = "id", unique = true, nullable = false)
    private long id;

    @Column(name = "device_id", length = 255, unique = false, nullable = false)
    private String deviceId;

    @Column(name = "latitude", unique = false, nullable = false)
    private float latitude;

    @Column(name = "longitude", unique = false, nullable = false)
    private float longitude;

    @Column(name = "accuracy", unique = false, nullable = false)
    private float accuracy;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "createtime", unique = false, nullable = false)
    private Date createTime;

    @Version
    private int version;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getDeviceId() {
        return deviceId;
    }

    public void setDeviceId(String deviceId) {
        this.deviceId = deviceId;
    }

    public float getLatitude() {
        return latitude;
    }

    public void setLatitude(float latitude) {
        this.latitude = latitude;
    }

    public float getLongitude() {
        return longitude;
    }

    public void setLongitude(float longitude) {
        this.longitude = longitude;
    }

    public float getAccuracy() {
        return accuracy;
    }

    public void setAccuracy(float accuracy) {
        this.accuracy = accuracy;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public double distanceFrom(Location location, DistanceUnit distanceUnit) {
        return distance(this.getLatitude(), this.getLongitude(), location.getLatitude(), location.getLongitude(), distanceUnit);
    }

    private double distance(double lat1, double lon1, double lat2, double lon2, DistanceUnit distanceUnit) {
        if (lat1 == lat2 && lon1 == lon2) {
            return 0;
        }

        double theta = lon1 - lon2;
        double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta));
        dist = Math.acos(dist);
        dist = rad2deg(dist);
        dist = dist * 60 * 1.1515;
        if (distanceUnit == KILOMETER) {
            dist = dist * 1.609344;
        } else if (distanceUnit == NAUTICAL_MILES) {
            dist = dist * 0.8684;
        }
        return (dist);
    }

    private double deg2rad(double deg) {
        return (deg * Math.PI / 180.0);
    }

    private double rad2deg(double rad) {
        return (rad * 180.0 / Math.PI);
    }

    @Override
    public String toString() {
        return "Location{" +
                "id=" + id +
                ", deviceId='" + deviceId + '\'' +
                ", latitude=" + latitude +
                ", longitude=" + longitude +
                ", accuracy=" + accuracy +
                ", createTime=" + createTime +
                ", version=" + version +
                '}';
    }

}
